package ex2a.controller;

import ex2a.gui.Frame;
import ex2a.gui.ProductsPanel;
import ex2a.gui.SemaphoresPanel;
import ex2a.gui.ThreadsPanel;
import ex2a.model.ThreadContext;
import ex2a.model.estruturas.FilaCircular;
import ex2a.model.semaforos.OpenSemaphore;
import ex2a.model.threads.ConsumerThread;
import ex2a.model.threads.GUIThread;
import ex2a.model.threads.ProducerThread;

public class Controller {
	
	private static final Controller singleton;
	
	private static final int numSemaphores = 3;
	
	// Semaforo 0 - Exclusao Mutua de acesso ao buffer. Inicializa com 1
	// Semaforo 1 - Controle de escrita no buffer, protege contra overflow. Inicializa com o tamanho do buffer
	// Semaforo 2 - Controler de leitura no buffer, protege contra underflow. Inicializa com 0
	
	@SuppressWarnings("unused")
	private Frame mainFrame;
	private SemaphoresPanel semaphoresPanel = null;
	private ThreadsPanel threadsPanel = null;
	private ProductsPanel productsPanel = null;
	private GUIThread updater = null;
	private boolean maximized = true, start = false;
	
	public int speed;
	public OpenSemaphore[] semaphores;
	public ConsumerThread[] consumers;
	public ProducerThread[] producers;
	public FilaCircular<Integer> fila;
	public boolean finish;
	
	private Controller() {}
	
	public static Controller getInstance() {
		return singleton;
	}
	
	public void init( String title, int consumers, int producers, int products, int limit ) {
		int i = 0;
		finish = false;
		// Tempo usado no sleep das threads
		speed = 500;
		
		// Inicializa o buffer
		fila = new FilaCircular<Integer>( products );
		
		// Aloca as Threads
		this.consumers = new ConsumerThread[consumers];
		this.producers = new ProducerThread[producers];
		
		// Inicializando os semaforos
		semaphores = new OpenSemaphore[ numSemaphores ];
		
		// Semaforo customizado que dá prioridades aos produtores
		semaphores[0] = new OpenSemaphore( 1, "MUTEX" );
		// Semaforo de controle de escrita
		semaphores[1] = new OpenSemaphore( products, "WRITE COUNTER" );
		// Semaforo de controler de leitura
		semaphores[2] = new OpenSemaphore( 0, "READ COUNTER" );
		
		for( i = 0; i < consumers; ++i ) {
			this.consumers[i] = new ConsumerThread( i, limit, 0, 1, 2 );
		}
		
		for( i = 0; i < producers; ++i ) {
			this.producers[i] = new ProducerThread( i, limit, 0, 1, 2  );
		}
		
		updater = new GUIThread();
		
		updater.start();
		
		mainFrame = new Frame( title );
		
		// Inicializa as threads
		for( i = 0; i < consumers; ++i ) {
			this.consumers[i].start();
		}
		
		for( i = 0; i < producers; ++i ) {
			this.producers[i].start();
		}
		
		for( i = 0; i < producers; ++i ) {
			try {
				this.producers[ i ].join();
			} catch (InterruptedException e) {}
		}
		
		for( i = 0; i < consumers; ++i ) {
			try {
				this.consumers[ i ].join();
			} catch (InterruptedException e) {}
		}
		finish = true;
		System.out.println( "Programa terminou..." );
	}
	
	public int getTotalProduzido() {
		return fila.getTotalProduzido();
	}
	
	public void setThreadsPanel( ThreadsPanel threadsPanel ) {
		this.threadsPanel = threadsPanel;
	}
	
	public void setProductsPanel( ProductsPanel productsPanel ) {
		this.productsPanel = productsPanel;
	}
	
	public void setSemaphoresPanel( SemaphoresPanel semaphoresPanel ) {
		this.semaphoresPanel = semaphoresPanel;
	}
	
	public ThreadContext[] getProducers() {
		return updater.getProducers();
	}
	
	public ThreadContext[] getConsumers() {
		return updater.getConsumers();
	}
	
	public Object[] getProducts() {
		return fila.getProducts();
	}
	
	public int[] getSemaphores() {
		return updater.getSemaphores();
	}
	
	public void updateSemaphoresPanel() {
		semaphoresPanel.repaint();
	}
	
	public void updateThreadsPanel() {
		threadsPanel.repaint();
	}
	
	public void updateProductsPanel() {
		productsPanel.repaint();
	}
	
	public boolean isRunning() {
		return maximized && start;
	}
	
	public void maximize( boolean maximized ) {
		if( maximized && start ) {
			synchronized( this ) {
				this.notifyAll();
			}
		}
		this.maximized = maximized;
	}
	
	public void start( boolean start ) {
		if( start ) {
			synchronized( this ) {
				this.notifyAll();
			}
		}
		this.start = start;
	}
	
	static {
		singleton = new Controller();
	}
}
